using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using DG.Tweening;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.PlayerLoop;
using UnityEngine.Serialization;
using UnityEngine.UI;
using Quaternion = UnityEngine.Quaternion;
using Random = UnityEngine.Random;
using Vector2 = UnityEngine.Vector2;
using Vector3 = UnityEngine.Vector3;

public class PlayerController : MonoBehaviour
{
    public static PlayerController Instance = null;

    [SerializeField] private JoystickManager _joystick;
    [SerializeField] private CharacterController _characterController;
    [SerializeField] private ParticleSystem _victoryParticle;
    [SerializeField] private Canvas _canvas;
    [SerializeField] private TextMeshProUGUI _timer;
    [SerializeField] private GuidePanel _guidePanel;
    [SerializeField] private DialogPanel _dialogPanel;

    private float _seconds;

    private Vector3 dir = Vector3.zero;

    public Vector3 Dir
    {
        get { return dir.normalized; }
        set
        {
            _isMove = value != Vector3.zero;
            if (value == Vector3.zero) return;
            dir = value;
        }
    }

    private bool _isMove = false;
    private bool _isPlayingFootStepAudio = false;

    private void Start()
    {
        if (Instance != null)
        {
            Instance = this;
        }

        InitWaterGun(GameManager.Instance.WaterGunIndex);
        InitNozzleBtns();
        InitMissonPrefabs();

        _seconds = 0;
        _canvas.gameObject.SetActive(true);

        _joystick.InitJoystick();

        GameManager.Instance.PlayBGAudio();

        if (GameManager.Instance.MissionIndex == 0)
        {
            _guidePanel.ShowGuide(() =>
            {
                _dialogPanel.gameObject.SetActive(true);
                _dialogPanel.ShowDialog(() => { FreeUsePanel.Show(Random.Range(1, 9), (index) => { InitWaterGun(index); }); });
            });
        }
        else
        {
            _dialogPanel.gameObject.SetActive(true);
            _dialogPanel.ShowDialog(() => { FreeUsePanel.Show(Random.Range(1, 9), (index) => { InitWaterGun(index); }); });
        }

        InvokeRepeating("AdAutoPopup", 30, 30);
    }

    private void OnDestroy()
    {
        CancelInvoke("AdAutoPopup");
    }

    private void AdAutoPopup() => AdManager.ShowNative();

    private float _time;

    private void FixedUpdate()
    {
        _seconds += Time.deltaTime;
        _timer.text = $"{((int)(_seconds / 60)).ToString("D2")}:{((int)(_seconds % 60)).ToString("D2")}";

        if (_isMove)
        {
            Move();
            if (!_isPlayingFootStepAudio)
            {
                GameManager.Instance.PlayFootAudio();
                _isPlayingFootStepAudio = true;
            }
        }
        else
        {
            _isPlayingFootStepAudio = false;
            GameManager.Instance.StopFootAudio();
        }

        if (_time < 0.1f)
        {
            _time += Time.deltaTime;
        }
        else
        {
            if (_isSpraying)
            {
                DrawRay(_angle, _isVertical);
            }

            _time = 0;
        }

        ShowHint();
    }

    private void Move()
    {
        _characterController.Move(transform.TransformDirection(Dir) * Time.deltaTime * Constants.PlayerMoveSpeed);
        _characterController.Move(Vector3.down);
    }

    #region Mission

    private List<CleanManager> _cleanManagers = new List<CleanManager>();

    private void InitMissonPrefabs()
    {
        List<GameObject> goes = new List<GameObject>();
        for (int i = 0; i < Constants.PathMissions.Count; i++)
        {
            GameObject go = GameManager.Instance.GetMissionPrefab(i);
            bool isCurMission = go.tag == GameManager.Instance._missionDatas[GameManager.Instance.MissionIndex].MissionName;
            go.transform.GetChild(isCurMission ? 0 : 1).gameObject.SetActive(true);
            go.transform.GetChild(isCurMission ? 1 : 0).gameObject.SetActive(false);
            goes.Add(go);
        }

        //重置主角位置
        transform.position = Constants.PlayerMissionPos[GameManager.Instance.MissionIndex];
        transform.LookAt(goes[GameManager.Instance.MissionIndex].transform.position);
        _cm = goes[GameManager.Instance.MissionIndex].transform.GetChild(0).GetComponent<CleanManager>();

        if (_cm == null)
        {
            _cm = goes[GameManager.Instance.MissionIndex].transform.GetChild(0).GetChild(0).GetComponent<CleanManager>();
        }

        _cm.Material.SetFloat("_Value", 1);
        _missionNameTxt.text = _cm.gameObject.tag;
        _missionProgressTxt.text = $"100%";
    }

    public void SetMissonCanvasActive(bool isActive) => _canvas.gameObject.SetActive(isActive);

    #endregion

    #region WaterGun

    private bool _needRotate = false;
    private bool _isSpraying = false;

    private string _sprayAudio;

    private Transform _sprayPos;
    [SerializeField] private Transform _nozzleBtnsTrans;
    private WaterGun _waterGun;

    private int _nozzleIndex;
    private List<NozzleBtn> _nozzleBtns = new List<NozzleBtn>();

    private void InitWaterGun(int index)
    {
        _isWin = false;
        if (_waterGun != null)
        {
            Destroy(_waterGun.gameObject);
            _waterGun = null;
        }

        GameManager.Instance.WaterGunIndex = index;

        //枪
        string gunPath = $"{Constants.PathGunPrefab}{index}";
        GameObject gunGo = GameManager.Instance.LoadPrefab(gunPath, true);
        gunGo.transform.SetParent(transform.Find("Main Camera/GunPos"));
        gunGo.name = gunGo.name.Split('(')[0];
        gunGo.transform.localPosition = Vector3.zero;
        gunGo.transform.localRotation = Quaternion.identity;
        _waterGun = gunGo.GetComponent<WaterGun>();
        SprayAngle_1();

        //粒子
        _sprayPos = _waterGun._sprayPos;
    }

    private void InitNozzleBtns()
    {
        _nozzleIndex = 0;

        for (int i = 0; i < 4; i++)
        {
            GameObject go = GameManager.Instance.LoadPrefab(Constants.PathNozzlePrefab);
            go.transform.SetParent(_nozzleBtnsTrans);
            go.transform.localScale = Vector3.one;
            NozzleBtn nozzleBtn = go.GetComponent<NozzleBtn>();
            nozzleBtn.Init(i < 2);
            nozzleBtn.Set(i, OnNozzleBtnClick);
            _nozzleBtns.Add(nozzleBtn);
        }

        for (int i = 0; i < _nozzleBtns.Count; i++)
        {
            _nozzleBtns[i].SetSelect(_nozzleIndex);
        }
    }

    private bool _unlock3 = false;
    private bool _unlock4 = false;

    private void OnNozzleBtnClick(int index)
    {
        GameManager.Instance.PlayUIAudio(Constants.PathAudio_BtnClick_1);

        _nozzleIndex = index;

        switch (index)
        {
            case 0:
                SprayAngle_1();
                break;
            case 1:
                SprayAngle_2();
                break;
            case 2:
                SprayAngle_3();
                break;
            case 3:
                SprayAngle_4();
                break;
        }


        for (int i = 0; i < _nozzleBtns.Count; i++)
        {
            _nozzleBtns[i].SetSelect(_nozzleIndex);
        }
    }

    public void RotateNozzle()
    {
        GameManager.Instance.PlayUIAudio(Constants.PathAudio_Nozzle);
        if (_angle == 0) return;

        _isVertical = !_isVertical;
        _waterGun.RotateNozzle(_isVertical);
    }

    public void SprayAngle_1()
    {
        _angle = 0;
        _isVertical = false;
        _waterGun.RotateNozzle(_isVertical);
        _sprayAudio = Constants.PathAudio_A;
        _waterGun.SetSprayType(WaterGun.SprayAngle.Angle_0);
    }

    public void SprayAngle_2()
    {
        _angle = 10;
        _sprayAudio = Constants.PathAudio_B;
        _waterGun.SetSprayType(WaterGun.SprayAngle.Angle_10);
    }

    public void SprayAngle_3()
    {
        _angle = 20;
        _sprayAudio = Constants.PathAudio_C;
        _waterGun.SetSprayType(WaterGun.SprayAngle.Angle_20);
    }

    public void SprayAngle_4()
    {
        _angle = 30;
        _sprayAudio = Constants.PathAudio_D;
        _waterGun.SetSprayType(WaterGun.SprayAngle.Angle_30);
    }

    public void OnSprayButtonDown(BaseEventData baseData)
    {
        GameManager.Instance.PlayAudio(_sprayAudio, true);
        _isSpraying = true;
        _waterGun.SetSprayActive(true);
        _joystick.OnLookClickDown(baseData);
    }

    public void OnSprayButtonUp()
    {
        GameManager.Instance.StopAudio();
        _isSpraying = false;
        _waterGun.SetSprayActive(false);
    }

    public void OnSprayButtonDrag(BaseEventData baseData)
    {
        _joystick.OnLookAreaDrag(baseData);
    }

    private void ShowLine(bool isShow, Vector3 starPos = new Vector3(), Vector3 targetPos = new Vector3())
    {
        if (!isShow) return;
    }

    #endregion

    #region Clean

    [Range(0, 90)] public float _angle = 0;
    private int _lineCount = 8;

    [SerializeField] private Texture2D _dirtBrush;
    [SerializeField] private Image _cleanRateImg;
    [SerializeField] private TextMeshProUGUI _missionNameTxt;
    [SerializeField] private TextMeshProUGUI _missionProgressTxt;

    public bool _isVertical;
    private CleanManager _cm;
    private Vector2Int lastPaintPixelPosition;

    private void DrawRay(float angle, bool isHorizontal)
    {
        int linePreAngle = (int)(angle / _lineCount);

        for (int i = _lineCount; i > 0; i--)
        {
            Vector3 fire = Quaternion.AngleAxis(-_lineCount / 2 * linePreAngle + (_lineCount - i) *
                linePreAngle, _isVertical ? transform.right : transform.up) * _sprayPos.transform.forward;
            Ray ray = new Ray(_sprayPos.transform.position, fire);

            CheckRay(ray);

            Debug.DrawRay(_sprayPos.transform.position, ray.direction * 10, Color.cyan);

            if (i <= 0) i = _lineCount;
        }
    }

    private bool _isWin;

    void CheckRay(Ray ray)
    {
        if (Physics.Raycast(ray, out RaycastHit hit, 5))
        {
            //生成粒子
            GameObject go = ObjectPool.GetInstance().SpawnObject(Constants.PathSmokeParticlePrefab);
            GameObject go_2 = ObjectPool.GetInstance().SpawnObject(Constants.PathSmokeParticlePrefab_2);
            Vector3 randomPos = new Vector3(Random.Range(0, 0.1f), Random.Range(0, 0.1f), Random.Range(0, 0.1f));
            go.transform.position = hit.point + hit.normal * .02f + randomPos;
            go.transform.forward = hit.normal;
            go_2.transform.position = go.transform.position;
            go_2.transform.forward = hit.normal;

            //清洗
            Vector2 textureCoord = hit.textureCoord; //碰撞位置的uv纹理坐标
            int pixelX = (int)(textureCoord.x * _cm.DirtMaskTexture.width);
            int pixelY = (int)(textureCoord.y * _cm.DirtMaskTexture.height);

            Vector2Int paintPixelPosition = new Vector2Int(pixelX, pixelY);
            //Debug.Log("UV: " + textureCoord + "; Pixels: " + paintPixelPosition);

            int paintPixelDistance = Mathf.Abs(paintPixelPosition.x - lastPaintPixelPosition.x) +
                                     Mathf.Abs(paintPixelPosition.y - lastPaintPixelPosition.y);

            int maxPaintDistance = 7;
            if (paintPixelDistance < maxPaintDistance)
            {
                return;
            }

            lastPaintPixelPosition = paintPixelPosition;

            float rate = 1 - _cm.DirtAmount / _cm.DirtAmountTotal;
            float rate2 = rate / ((float)(100 - Constants.MissonRate[GameManager.Instance.MissionIndex]) / 100);
            int compensate = (int)(rate2 * Constants.MissonRate[GameManager.Instance.MissionIndex]);
            int clearRate = Mathf.Clamp(100 - (int)(rate * 100) - compensate, 0, 100);
            _cleanRateImg.fillAmount = (float)clearRate / 100;
            _missionProgressTxt.text = $"{clearRate}%";

            if (_isWin) return;

            if (clearRate <= 0)
            {
                _isWin = true;
                _cm.CleanAllDirt();
                GameManager.Instance.MissionTime = (int)_seconds;
                _victoryParticle.Play();
                _isMove = false;
                GameManager.Instance.PlayWinAudio();
            }

            int pixelXOffset = pixelX - _dirtBrush.width / 2;
            int pixelYOffset = pixelY - _dirtBrush.height / 2;

            for (int x = 0; x < _dirtBrush.width; x++)
            {
                for (int y = 0; y < _dirtBrush.height; y++)
                {
                    Color pixelDirt = _dirtBrush.GetPixel(x, y);
                    Color pixelDirtMask = _cm.DirtMaskTexture.GetPixel(pixelXOffset + x, pixelYOffset + y);

                    float removedAmount = pixelDirtMask.g - (pixelDirtMask.g * pixelDirt.g);
                    _cm.DirtAmount -= removedAmount;

                    _cm.DirtMaskTexture.SetPixel(
                        pixelXOffset + x,
                        pixelYOffset + y,
                        new Color(0, pixelDirtMask.g * pixelDirt.g, 0)
                    );
                }
            }

            _cm.DirtMaskTexture.Apply();
        }
    }

    #endregion

    #region Hint

    private bool isShowHint = false;

    private float value = 1;
    private float hintTime = 3;
    private bool isReverse = false;

    public void ShowHint()
    {
        if (!isShowHint) return;

        if (hintTime <= 0)
        {
            value = 1;

            _cm.Material.SetFloat("_Value", value);

            hintTime = 3;
            isShowHint = !isShowHint;
            return;
        }

        if (!isReverse)
        {
            value += Time.deltaTime * 10;
        }
        else
        {
            value -= Time.deltaTime * 10;
        }

        if (value > 6)
        {
            isReverse = true;
        }

        if (value < 1)
        {
            hintTime -= 1;
            isReverse = false;
        }

        _cm.Material.SetFloat("_Value", value);
    }

    public void OnHintBtnClick()
    {
        GameManager.Instance.PlayUIAudio(Constants.PathAudio_BtnClick);
        isShowHint = true;
    }

    #endregion

    public void OnPauseBtnClick()
    {
        GameManager.Instance.PlayUIAudio(Constants.PathAudio_BtnClick);
        TipPanel.Instance.ShowPanel("游戏暂停", "休息一下。",
            () => { GameManager.Instance.LoadScene(Constants.StartScene); }, null, "回到主菜单", "继续");
    }
}